package com.atguigu.tingshu.album.service.impl;

import com.atguigu.tingshu.album.service.TestService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * @author: atguigu
 * @create: 2023-10-07 08:54
 */
@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    //锁名称
    private static final String lockKey = "lock";
    //锁值
    //private static final String lockVal = "lock";


    /**
     * SpringDataRedis实现分布式锁
     */
    //@Override
    //public void testLock() {
    //    try {
    //        //获取到分布式锁 版本1：setnx命令实现
    //        //Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(lockKey, lockVal);
    //        //为锁设置过期时间 版本二：setnx expire 两个命令 不具备原子性   利用：set命令 + 参数优化
    //        //stringRedisTemplate.expire(lockKey, 5, TimeUnit.SECONDS);
    //
    //        //版本三：锁的值中加入线程信息，解决业务超时，锁互相释放问题  uuid值存在Java栈中（线程私有）
    //        String lockVal = IdUtil.fastSimpleUUID();
    //        Boolean flag = redisTemplate.opsForValue().setIfAbsent(lockKey, lockVal, 5, TimeUnit.SECONDS);
    //
    //        //获取锁成功才能执行业务
    //        if (flag) {
    //            try {
    //
    //                //1.从Redis缓存中获取key="num"的值  保证redis中存在"num"(手动提前在redis中创建key)
    //                String value = redisTemplate.opsForValue().get("num");
    //                if (StringUtils.isBlank(value)) {
    //                    return;
    //                }
    //                //2.对获取到值进行+1操作
    //                int num = Integer.parseInt(value);
    //                redisTemplate.opsForValue().set("num", String.valueOf(++num));
    //            } finally {
    //                //释放锁 从Redis中获取锁的值 跟 当前线程锁值比较 相同在释放锁
    //                //if (redisTemplate.opsForValue().get(lockKey).equals(lockVal)) {
    //                //    redisTemplate.delete(lockKey);
    //                //}
    //                //版本四：通过Lua脚本进行解锁
    //                String text = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
    //                        "then\n" +
    //                        "    return redis.call(\"del\",KEYS[1])\n" +
    //                        "else\n" +
    //                        "    return 0\n" +
    //                        "end";
    //                //创建Redis脚本对象，封装lua脚本
    //                DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
    //                redisScript.setScriptText(text);
    //                redisScript.setResultType(Long.class);
    //                Long flag1 = redisTemplate.execute(redisScript, Arrays.asList(lockKey), lockVal);
    //                if (flag1.intValue() == 0) {
    //                    System.out.println("释放锁失败");
    //                }
    //
    //            }
    //        } else {
    //            //获取锁失败，业务场景：要求业务逻辑代码必然被执行 加入自旋逻辑
    //            Thread.sleep(300);
    //            this.testLock();
    //        }
    //    } catch (Exception e) {
    //        e.printStackTrace();
    //    }
    //}


    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void testLock() {
        try {
            //先尝试获取分布式锁 1.创建锁对象  2.调用获取锁方法
            RLock lock = redissonClient.getLock("lock");

            //获取锁成功后，才执行业务
            lock.lock();  //阻塞到获取锁成功为止
            //lock.tryLock(5, TimeUnit.SECONDS);  //参数1：等待锁获取最大时间  参数2：时间单位  获取锁失败返回false
            //lock.tryLock(3, 5, TimeUnit.SECONDS);//参数1：等待锁获取最大时间  参数2：锁过期时间，参数3：时间单位  获取锁失败返回false

            try {
                //1.从Redis缓存中获取key="num"的值  保证redis中存在"num"(手动提前在redis中创建key)
                String value = redisTemplate.opsForValue().get("num");
                if (StringUtils.isBlank(value)) {
                    return;
                }
                //2.对获取到值进行+1操作
                int num = Integer.parseInt(value);
                redisTemplate.opsForValue().set("num", String.valueOf(++num));
            } finally {
                //业务执行完毕释放锁
                lock.unlock();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 查询数据
     *
     * @return
     */
    @Override
    public String read() {
        //1.创建读写锁对象
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("myLock");
        //2.获取读锁
        RLock rLock = rwlock.readLock();
        rLock.lock(5, TimeUnit.SECONDS);
        //3.执行业务处理
        System.out.println("查询数据库中用户表中用户记录");

        //rLock.unlock();
        return null;
    }

    /**
     * 修改数据
     *
     * @return
     */
    @Override
    public String write() {
        //1.创建读写锁对象
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("myLock");
        //2.获取读锁
        RLock wLock = rwlock.writeLock();
        wLock.lock(5, TimeUnit.SECONDS);
        //3.执行业务处理
        System.out.println("正在修改数据");

        //rLock.unlock();
        return null;
    }
}
